#include "xthread.h"
#include "xrt_error.h"
#include "xassert.h"
#include "xsys_log.h"
#include "xthread_manager.h"

namespace xge{
namespace xge_internal{

  ThreadHandler::ThreadHandler (
    const String& name,
    ::HANDLE handler 
    ) : name_(name) , thread_handler_(handler) {}

  ThreadHandler::~ThreadHandler() {
    // We can not delete the thread which is running
    // however , we have used reference count
    // so there is no need to assert(!IsRunning());
    // and the reference running may be zero when IsRunning is true
    // but reference count goes to zero when we do not need the
    // XThread !
    SYS_LOG(INFO,false,_xT("A ThreadHandler %s has been deleted!"),name_.c_str());
    Clear();
  }

  void ThreadHandler::Clear() {
    if( thread_handler_ != INVALID_HANDLE_VALUE ) {
      if( ::CloseHandle(thread_handler_) == FALSE ) {
        // Using warning version will not hurt the execution of
        // program , maybe such fail will not hurt the program 
        // at surface 
        WIN32_API_WARNING(
          _xT("CloseHandle")
          );
      }
      thread_handler_ = INVALID_HANDLE_VALUE;
    }
  }

  /*
  bool ThreadHandler::Kill() {
    XASSERT(IsValid());
    // The termination of a thread is ASYNCHORNIZED 
    // The msdn describe it a little bit vague .
    // I try many times and find it is not Terminate the target thread
    // at once . 
    // Listen: This function is extremely unsafe to use , whatever we done
    // here ,we will be very possible to cause very very serious problem
    // so we record any call to Kill as a PANIC 
    // Since after this calling our SYS_LOG may encounter a dead lock  ( it is very easy to cause a dead lock here )
    // so we must call it at start .

    SYS_LOG(PANIC,false,_xT("The thread: %s has been terminated by force!"),name_.c_str());

    // Devil is running now .
    if( ::TerminateThread(thread_handler_,ABNORMAL_EXIT_CODE) == FALSE ) {
      WIN32_API_WARNING(
        _xT("TerminateThread")
        );
      return false;
    }
    // We just wait until the thread finish
    DWORD ret = ::WaitForSingleObject(thread_handler_,INFINITE) ;
    if( ret != WAIT_OBJECT_0 ) {
      // Error comes 
      WIN32_API_WARNING(_xT("WaitForSingleObject"));
      return false;
    }
    // Clear the win32 handler
    Clear();
    XThreadManager::NotifyThreadTermination(name_);
    return true;
  }
  */

  size_t ThreadHandler::GetUniqueThreadID() {
    XASSERT(IsValid());
    return (size_t)(::GetThreadId(thread_handler_));
  }

  const TripleResult ThreadHandler::Join() {
    XASSERT(IsValid());
    // Join this thread
    const DWORD ret = ::WaitForSingleObject( thread_handler_ , INFINITE );
    switch( ret ) {
      case WAIT_OBJECT_0:
        // Clear the stuff
        Clear();
        return TripleResult::kSucceded;
      default:
        WIN32_API_WARNING(_xT("WaitForSingleObject"));
        return TripleResult::kException;
    }
  }

  const TripleResult ThreadHandler::Join( int ms ) {
    XASSERT(IsValid());
    // Join this thread
    const DWORD ret = ::WaitForSingleObject( thread_handler_ , (DWORD)ms );
    switch( ret ) {
      case WAIT_OBJECT_0:
        // Clear the stuff
        Clear();
        return TripleResult::kSucceded;
      case WAIT_TIMEOUT:
        return TripleResult::kFailed;
      default:
        WIN32_API_WARNING(_xT("WaitForSingleObject"));
        return TripleResult::kException;
    }
  }


  void ThreadHandler::SetDebugName( const char* ansi_debug_name ) {
#ifdef _DEBUG
    // The information on how to set the thread name comes from
    // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
    static const DWORD kVCThreadNameException = 0x406D1388;

    typedef struct tagTHREADNAME_INFO {
      DWORD dwType;  // Must be 0x1000.
      LPCSTR szName;  // Pointer to name (in user addr space).
      DWORD dwThreadID;  // Thread ID (-1=caller thread).
      DWORD dwFlags;  // Reserved for future use, must be zero.

    } THREADNAME_INFO;

    if( !::IsDebuggerPresent() || !IsValid() ) {
      return;
    }

    THREADNAME_INFO info;
    info.dwType = 0x1000;
    info.szName = (LPCSTR)( ansi_debug_name );
    info.dwThreadID = ::GetThreadId( thread_handler_ );
    info.dwFlags = 0;
    __try {
      RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
        reinterpret_cast<DWORD_PTR*>(&info));
    } __except(EXCEPTION_CONTINUE_EXECUTION) {
      SYS_LOG(PANIC,true,_xT("Win32API::RaisException failed!Error code: %d"),::GetLastError());
    }
#else// _DEBUG
    DUMMY_ARG(ansi_debug_name);
#endif
  }

  const TripleResult ThreadHandler::IsRunning() const {
    if( !IsValid() ) {
      return TripleResult::kFailed;
    }
    DWORD exit_code ;
    if( ::GetExitCodeThread(thread_handler_,&exit_code) == FALSE ) {
      WIN32_API_WARNING(_xT("GetExitCodeThread"));
      return TripleResult::kException;
    }
    if( exit_code == STILL_ACTIVE ) {
      return TripleResult::kSucceded;
    } else {
      return TripleResult::kFailed;
    }
  }

  bool ThreadHandler::SuspendXThread( size_t* calling_count ) {
    XASSERT( IsValid() );
    DWORD ret = ::SuspendThread( thread_handler_ );
    if( ret != (DWORD)(-1) ) {
      if(calling_count) 
        *calling_count = ret;
      return true;
    } else {
      WIN32_API_WARNING(_xT("SuspendThread"));
      return false;
    }
  }

  bool ThreadHandler::ResumeXThread( size_t* calling_count ) {
    XASSERT( IsValid() );
    DWORD ret = ::ResumeThread( thread_handler_ );
    if( ret != (DWORD)(-1) ) {
      if(calling_count) 
        *calling_count = ret;
      return true;
    } else {
      WIN32_API_WARNING(_xT("ResumeXThread"));
      return false;
    }
  }


} // namespace xge_internal

}// namespace xge